[アップデート]AWS Control TowerにOUで有効化されているコントロールへタグ付けできるAPIが追加されました。
あしざわです。
本日、AWS Control TowerのAPIにアップデートがあり、OUで有効化されているコントロールへタグ付けできる新しいAPIが追加されました。
追加されたAPIは以下3つです。
- ListTagsForResource
- TagResource
- UntagResource
これまでに公開されていたControl Tower APIは以下の5つだったので、今回のアップデートによって合計で8つになりました。
- DisableControl
- EnableControl
- GetControlOperation
- GetEnabledControl
- ListEnabledControls
GetEnabledControlは直近で追加されたAPIで、以下ブログで紹介されています。
新規のAPIは公式ドキュメントのAPIリファレンスにも追加されていますが、現時点でAWS CLI(v2)への反映はされていませんでした(後述します)
このブログでは、AWSマネジメントコンソール および AWS CLIで追加されたAPIの動作を検証してみます。
4行まとめ
- タグ付与できるコントロールはOUに対して有効化(関連付け)されているもののみ。
- Control Towerの
マネジメントコンソール > コントロールライブラリ
の各コントロールの詳細画面から、コントロールが有効化されているOU単位でタグ追加/削除/タグ確認ができる - AWS CLIでも同様にタグ追加/削除/タグ確認ができる(ただし現時点ではAWS CLIv2ではサポートされておらず、AWS CLIv1から実行する必要がある。)
- Control Towerはタグエディタのサポート範囲外なので、タグの一括検索やタグポリシーなどタグの管理機能が利用できない。
AWSマネジメントコンソールでの検証
Control Towerのマネジメントコンソールのコントロールライブラリから、[AWS-GR_LOG_GROUP_POLICY] AWS Control Tower によって設定された Amazon CloudWatch Logs ロググループの変更を許可しない』を検索し、コントロールの詳細画面を確認します。
このコントロールは必須コントロールと呼ばれる無効化できないコントロールで、ランディングゾーンが有効なすべてのOUで有効化されているものです。
コントロールの詳細画面 「OUは有効です」タブの 設定 > View tags
からタグを確認できるようです。
デフォルトでは何も設定されていないので Edit tags
でタグを追加してみます。
Key: Status, Value: Required タグを設定して、Save chagesをクリックします。
再度View tags
を確認すると、Statusタグが追加されていました。
追加したタグはRemoveから削除できます。
Undoもできるようです。謝って削除してしまっても安心です。
再度削除して、Save chagesをクリックした後、View tags
を確認するとタグが削除されていました。
AWS CLIでの検証
続いて、AWS CLIから試してみます。
2023年11月11日未明時点では、AWS CLI(v2)で当該APIは未サポートの状態だったので、AWS CLI(v1)で検証を行います。
※以下プルリクエストがマージされ次第、v2でもサポートされるはずです
検証はCloudShell環境にて実施します。CloudShellではデフォルトでAWS CLI(v1)が導入されていなかったため、以下ブログを参考にAWS CLIのv1、v2がどちらも利用できる環境を準備しました。
ブログ執筆時とはpythonのバージョンが異なるため、インストールコマンド実行時にpython3
を明示的に指定してください。
python3 ./awscli-bundle/install -i ~/.local/aws-cli-v1 -b ~/.local/bin/aws-v1
以下コマンドでエラーが出なければ準備完了です。
aws-v1 --version
今回追加されたコントロールへのタグ付け関連のAPI実行時のパラメータとして、いずれかのOUで有効化されているコントロールのARNを渡す必要があります。
このARNをtargetIdentifierと呼びますが、似たような識別子にcontrolIdentifierがあります。
これらの違いは以下です (Control TowerのAWS CLI(v1)のコマンドリファレンスに記載あり)
- OUに関連付けられた状態のコントロール(targetIdentifier)
- OUに関連付けられていない状態のコントロール(controlIdentifier)
前者のtargetIdentifierの情報を得るために、以下の順で特定のOUで有効化されているコントロールの一覧を取得します。
- OrganizationsのRootOUのIDを調べる
- 特定のOU名(SecurityOUなど)のARNを調べる
- そのOUで有効化されているコントロールの一覧を取得する
こちらのスクリプトを実行すると1〜3をまとめてチェックでき、SecurityOUで有効化されているコントロールの一覧がJSON形式で表示されます。.Name == "SecurityOU"
の箇所を任意のOU名に書き換えることで、他のOUにも対応できます。
## RootOUのIDを取得する parentsid=$(aws-v1 organizations list-roots | jq -r '.Roots[0].Id') ## Nameに入力したOUのARNを取得する ouarn=$(aws-v1 organizations list-organizational-units-for-parent --parent-id $parentsid | jq -r '.OrganizationalUnits[] | select(.Name == "SecurityOU") | .Arn') ## OUで有効化されているコントロールの一覧を取得 aws-v1 controltower list-enabled-controls --target-identifier $ouarn
出力結果の抜粋がこちらです。
ハイライト箇所ののパラメータがtargetIdentifierです。こちらをメモしておきましょう。
・出力例(抜粋) { "enabledControls": [ 〜略〜 { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO957KTEXOXWC", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_LOG_GROUP_POLICY", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/49ZG5EDWJK64GCJ6", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_REGION_DENY", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, } 〜略〜 ] }
出力結果の全体は長いのでトグルに
{ "enabledControls": [ { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/49ZG5BUZNKOH963E", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_AUDIT_BUCKET_DELETION_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/4T8DO7RO0BV1MXSQ", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_AUDIT_BUCKET_PUBLIC_READ_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/24ZQ2V3TS0RD5TWE", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_AUDIT_BUCKET_PUBLIC_WRITE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/49ZG5BZJ7KS0WSX1", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CLOUDTRAIL_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/9MGRC0BGGHFI9O4O", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CLOUDTRAIL_CLOUDWATCH_LOGS_ENABLED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/4T8DO0B4WBYWJTDN", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CLOUDTRAIL_ENABLED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/4T8DO0G74SMHBU5Y", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CLOUDTRAIL_VALIDATION_ENABLED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/49ZG5CHTDSBEUFQK", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CLOUDWATCH_EVENTS_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/9MGRC1F2OVBHTCPL", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CONFIG_AGGREGATION_AUTHORIZATION_POLICY", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/24ZQ2ODLKW44ARF5", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CONFIG_AGGREGATION_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO3Y4G2RXZIAS", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CONFIG_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO4JI8QQJLADT", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CONFIG_ENABLED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/17B3F0B8CE7IWLDP", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CONFIG_RULE_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/8JYWAQFH1CMR4PDD", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CT_AUDIT_BUCKET_ENCRYPTION_CHANGES_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO61XCBXG6VCP", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CT_AUDIT_BUCKET_LIFECYCLE_CONFIGURATION_CHANGES_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/9MGRC3ACWD6U6SG8", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CT_AUDIT_BUCKET_LOGGING_CONFIGURATION_CHANGES_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO762OAKTJH8J", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_CT_AUDIT_BUCKET_POLICY_CHANGES_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/2EM6UC4UODS8SWI7", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_DETECT_CLOUDTRAIL_ENABLED_ON_SHARED_ACCOUNTS", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/8JYWARI9Z4RMJKTP", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_IAM_ROLE_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO8JC023MDCC1", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_LAMBDA_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO957KTEXOXWC", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_LOG_GROUP_POLICY", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/49ZG5EDWJK64GCJ6", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_REGION_DENY", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/LNJPIAU43NUAMNME", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_SNS_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" }, { "arn": "arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/2EM6U19V8W7WPTLE", "controlIdentifier": "arn:aws:controltower:ap-northeast-1::control/AWS-GR_SNS_SUBSCRIPTION_CHANGE_PROHIBITED", "driftStatusSummary": { "driftStatus": "NOT_CHECKING" }, "statusSummary": { "status": "SUCCEEDED" }, "targetIdentifier": "arn:aws:organizations::123456789012:ou/o-1vz68cxxak/ou-zbrf-rcjy07p1" } ] }
これまでに、特定のOUで有効化されているコントロールの中からタグ関連APIの実行対象になるコントロールのARNを取得しました。ここからは本題のタグ関連のAPIをAWS CLIで検証します。
当該のAPIはAWS CLIの以下コマンドで実行できます。
- aws controltower list-tags-for-resource 〜
- aws controltower tag-resource 〜
- aws controltower untag-resource 〜
list-tags-for-resource(タグの一覧表示)
一覧表示したコントロールから、"arn"
の値を取得して、以下list-tags-for-resource
コマンドの< >箇所と置き換えて実行してください。
タグが設定されていない時は空の配列が出力されます。
aws-v1 controltower list-tags-for-resource \ --resource-arn <OUで有効化済みのコントロールのarn>
・出力結果例 { "tags": {} }
tag-resource(タグの付与)
以下tag-resource
コマンドを実行して、タグを付与します。
"Env": "Prod"
の組み合わせのタグを設定してみます。
aws-v1 controltower tag-resource \ --resource-arn <OUで有効化済みのコントロールのarn> \ --tags '{"<タグ1のキー>": "<タグ1のバリュー>"}'
・出力結果例 なし
再度list-tags-for-resource
コマンドを実行して、結果を確認します。
Envタグが追加されていますね。
[cloudshell-user@ip-10-2-40-152]$ aws-v1 controltower list-tags-for-resource \ --resource-arn arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO957KTEXOXWC { "tags": { "Env": "Prod" } }
タグを2つ以上付与するときは、このようにカンマ区切りで続けてください。
aws-v1 controltower tag-resource \ --resource-arn <OUで有効化済みのコントロールのarn> \ --tags '{"<タグ1のキー>": "<タグ1のバリュー>", "<タグ2のキー>": "<タグ2のバリュー>"}'
untag-resource(タグの削除)
以下untag-resource
コマンドを実行して、タグを削除します。
aws-v1 controltower untag-resource \ --resource-arn <OUで有効化済みのコントロールのarn> \ --tag-keys '<タグのキー>'
・出力結果例 なし
list-tags-for-resource
コマンドを実行して、結果を確認します。
Envタグが削除されていることが確認できました。
[cloudshell-user@ip-10-2-40-152]$ aws-v1 controltower untag-resource \ --resource-arn arn:aws:controltower:ap-northeast-1:123456789012:enabledcontrol/J8XIO957KTEXOXWC \ --tag-keys 'Env' { "tags": {} }
タグを2つ以上まとめて削除する時は、このようにキーをスペース区切りで続けてください。
aws-v1 controltower untag-resource \ --resource-arn <OUで有効化済みのコントロールのarn> \ --tag-keys '<タグ1のキー>' '<タグ2のキー>'
検証は以上です。
その他
Control Towerがタグ付けをサポートしたのでもしかして...と期待して、タグエディタがサポートするリソースの詳細が記載された以下公式ドキュメントを確認したところ、Control Towerサービスの追加はありませんでした。残念。
タグエディタをコンソール上で確認しても想定通り、リソースタイプにControl Towerは表示されませんでした。今後のアップデートに期待したいです。
最後に
今回はAWS Control Towerに新規追加されたタグ付けに関連する3つのAPIについて紹介しました。
これまでは、Control Towerの管理はマネジメントコンソール上で行うことがほとんどだったと思います。今後もより便利なAPIが出て運用を楽にしてくれると嬉しいなと思った筆者でした。
APIの検証を通じて改めてControl TowerのコンソールやAPIには伸び代がたくさんあるな、と感じました。これからもアップデートにキャッチアップしながらControl Towerの未来について考えていきたいです。
以上です。